/*
 * Copyright (C) 2020 Agrui
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <atomic>
#include "Atomic.h"
#include "OSMemory.h"
#include "ParameterCheck.h"

typedef struct {
    std::atomic<int32_t>* value;
} AtomicObjType;

#define ATOMIC_OBJ_TO_ATOMIC_HANDLER(x) (AtomicType*)(x)
#define ATOMIC_HANDLER_TO_ATOMIC_OBJ(x) (AtomicObjType*)(x)

AtomicType* newAtomic(int32_t value) {
    AtomicObjType* obj = OS_MALLOC_TYPE(AtomicObjType);
    PARAMTER_CHECK_RETURN_NULL(obj);
    obj->value = new std::atomic<int>(value);
    return ATOMIC_OBJ_TO_ATOMIC_HANDLER(obj);
}

int32_t atomicAdd(AtomicType *atomic, int32_t amount) {
    AtomicObjType* obj = ATOMIC_HANDLER_TO_ATOMIC_OBJ(atomic);
    PARAMTER_CHECK_RETURN_VALUE(obj && obj->value, 0);
    int32_t origin = obj->value->fetch_add(amount);
    return origin + amount;
}

int32_t atomicInc(AtomicType *atomic) {
    AtomicObjType* obj = ATOMIC_HANDLER_TO_ATOMIC_OBJ(atomic);
    PARAMTER_CHECK_RETURN_VALUE(obj && obj->value, 0);
    return (*obj->value)++;
}


int32_t atomicSub(AtomicType *atomic, int32_t amount) {
    AtomicObjType* obj = ATOMIC_HANDLER_TO_ATOMIC_OBJ(atomic);
    PARAMTER_CHECK_RETURN_VALUE(obj && obj->value, 0);
    int32_t origin = obj->value->fetch_sub(amount);
    return origin - amount;
}

int32_t atomicDec(AtomicType *atomic) {
    AtomicObjType* obj = ATOMIC_HANDLER_TO_ATOMIC_OBJ(atomic);
    PARAMTER_CHECK_RETURN_VALUE(obj && obj->value, 0);
    return (*obj->value)--;
}

int32_t atomicRead(const AtomicType *atomic) {
    AtomicObjType* obj = ATOMIC_HANDLER_TO_ATOMIC_OBJ(atomic);
    PARAMTER_CHECK_RETURN_VALUE(obj && obj->value, 0);
    return obj->value->load();
}

void deleteAtomic(AtomicType *atomic) {
    AtomicObjType* obj = ATOMIC_HANDLER_TO_ATOMIC_OBJ(atomic);
    PARAMTER_CHECK_RETURN_VOID(obj);
    if (obj->value) {
        delete obj->value;
    }
    OS_FREE(obj);
}

